package net.lindeer.lang

class RoxClass(val name: String,
               val superclass: RoxClass?,
               private val methods: Map<String, RoxFunction>) : RoxCallable {

    override val arity
        get() = findMethod("init")?.arity ?: 0

    override fun call(interpreter: Interpreter, arguments: List<Any?>): Any? {
        return RoxInstance(this).also {
            findMethod("init")?.bind(it)?.call(interpreter, arguments)
        }
    }

    override fun toString(): String {
        return "{$name}"
    }

    fun findMethod(name: String): RoxFunction? {
        return methods[name] ?: superclass?.findMethod(name)
    }
}

class RoxInstance(private val clazz: RoxClass) {
    private val fields = mutableMapOf<String, Any?>()

    fun get(name: Token): Any? {
        val fieldName = name.lexeme
        if (fields.containsKey(fieldName)) {
            return fields[fieldName]
        }
        val method = clazz.findMethod(name.lexeme)
        if (method != null) {
            return method.bind(this)
        }
        throw RuntimeError(name, "Undefined property '$fieldName' of ${toString()}!")
    }

    fun set(name: Token, value: Any?) {
        fields[name.lexeme] = value
    }

    override fun toString(): String {
        return "${clazz.name}@0x${Integer.toHexString(hashCode())}"
    }
}
